#ifndef __ROOM_HPP
#define __ROOM_HPP

#include "util.hpp"
#include "online.hpp"
#include "db.hpp"
#include <vector>
#include <memory>

#define BOARD_ROW 15
#define BOARD_COL 15

#define CHESS_WHITE 1
#define CHESS_BLACK 2

typedef enum 
{
    GAME_START,
    GAME_OVER,
}room_statu;

class room
{
public:
    room(uint64_t room_id,user_table *tb_user,online_manager *online_user):
    _room_id(room_id),_statu(GAME_START),_player_count(0),_tb_user(tb_user),_online_user(online_user),_board(BOARD_ROW,std::vector<int>(BOARD_COL,0))
    {
        DLOG("%lu 房间创建成功！！！！",_room_id);
    }

    ~room()
    {
        DLOG("%lu 房间销毁成功！！！！",_room_id);
    }

    uint64_t id() { return _room_id; }
    room_statu statu() { return _statu; }
    int player_count() { return _player_count; }

    void add_white_user(uint64_t uid)
    { 
        _white_id = uid; 
        _player_count++;
    }

    void add_black_user(uint64_t uid)
    { 
        _black_id = uid; 
        _player_count++;
    }

    uint64_t get_white_user() { return _white_id; }
    uint64_t get_black_user() { return _black_id; }

    Json::Value handle_chess(Json::Value &req);//下棋动作
    Json::Value handle_chat(Json::Value &req);//聊天
    void handle_exit(uint64_t uid);//玩家退出

    void handle_request(Json::Value &req);//区分请求类型，根据不同的请求调用不同的处理函数
    void broadcast(Json::Value &req);//广播信息到房间所有客户端

private:
    bool five(int row, int col, int row_off,int col_off, int color);
    uint64_t check_win(int row, int col, int color);
private:
    uint64_t _room_id;
    room_statu _statu;

    int _player_count;
    uint64_t _white_id;
    uint64_t _black_id;

    user_table *_tb_user;
    online_manager *_online_user;

    std::vector<std::vector<int>> _board;
};


//待优化，过于冗余
///////////////////////////////////////////////////////////////////////////////////////////////
// Json::Value room::handle_chess(Json::Value &req)//下棋动作
// {
//     Json::Value json_resp = req;
//     //1.当前请求的房间号是否于当前的房间的房间号是否匹配
//     uint64_t room_id = req["room_id"].asUInt64();
//     if(room_id != _room_id)
//     {
//         json_resp["optype"] = "put_chess";
//         json_resp["result"] = false;
//         json_resp["reason"] = "房间号不匹配";

//         return json_resp;
//     }

//     //2.判断房间中两个玩家是否都在线，不在线的方失败
//     uint64_t cur_uid = req["uid"].asUInt64();
//     int chess_row = req["row"].asInt();
//     int chess_col = req["col"].asInt();
//     if(_online_user->is_in_game_room(_white_id) == false)
//     {
//         json_resp["optype"] = "put_chess";
//         json_resp["result"] = true;
//         json_resp["reason"] = "对手逃跑，获胜！！";
//         json_resp["room_id"] = _room_id;
//         json_resp["uid"] = cur_uid;
//         json_resp["row"] = chess_row;
//         json_resp["col"] = chess_col;
//         json_resp["winner"] = _black_id;

//         _tb_user->win(_black_id);
//         _tb_user->lose(_white_id);

//         return json_resp;
//     }

//     if(_online_user->is_in_game_room(_black_id) == false)
//     {
//         json_resp["optype"] = "put_chess";
//         json_resp["result"] = true;
//         json_resp["reason"] = "对手逃跑，获胜！！";
//         json_resp["room_id"] = _room_id;
//         json_resp["uid"] = cur_uid;
//         json_resp["row"] = chess_row;
//         json_resp["col"] = chess_col;
//         json_resp["winner"] = _white_id;

//         //uint64_t loser_id = _black_id;
//         _tb_user->win(_white_id);
//         _tb_user->lose(_black_id);

//         return json_resp;
//     }

//     //3.获取下棋的位置，并且判断下棋的位置是否合理
//     if(_board[chess_row][chess_col] != 0)
//     {
//         json_resp["optype"] = "put_chess";
//         json_resp["result"] = false;
//         json_resp["reason"] = "当前位置已经有棋子！！！";
//         return json_resp;
//     }

//     int cur_color = cur_uid == _white_id ? CHESS_WHITE: CHESS_BLACK;
//     _board[chess_row][chess_col] = cur_color;
//     //4.判断是否有玩家胜利
//     uint64_t winner_id = check_win(chess_row,chess_col,cur_color);

//     if(winner_id != 0)
//     {
//         //更新数据库用户信息
//         uint64_t loser_id = winner_id == _white_id ? _black_id : _white_id;
//         _tb_user->win(winner_id);
//         _tb_user->lose(loser_id);
//     }

//     json_resp["optype"] = "put_chess";
//     json_resp["result"] = true;
//     json_resp["reason"] = "获胜！！";
//     json_resp["room_id"] = _room_id;
//     json_resp["uid"] = cur_uid;
//     json_resp["row"] = chess_row;
//     json_resp["col"] = chess_col;
//     json_resp["winner"] = winner_id;
//     return json_resp;
// }

Json::Value room::handle_chess(Json::Value &req)//下棋动作
{
    Json::Value json_resp = req;

    //2.判断房间中两个玩家是否都在线，不在线的方失败
    uint64_t cur_uid = req["uid"].asUInt64();
    int chess_row = req["row"].asInt();
    int chess_col = req["col"].asInt();
    if(_online_user->is_in_game_room(_white_id) == false)
    {
        json_resp["result"] = true;
        json_resp["reason"] = "对手逃跑，获胜！！";
        json_resp["winner"] = (Json::UInt64)_black_id;
        return json_resp;
    }

    if(_online_user->is_in_game_room(_black_id) == false)
    {
        json_resp["result"] = true;
        json_resp["reason"] = "对手逃跑，获胜！！";
        json_resp["winner"] = (Json::UInt64)_white_id;
        return json_resp;
    }

    //3.获取下棋的位置，并且判断下棋的位置是否合理
    if(_board[chess_row][chess_col] != 0)
    {
        json_resp["result"] = false;
        json_resp["reason"] = "当前位置已经有棋子！！！";
        return json_resp;
    }

    int cur_color = cur_uid == _white_id ? CHESS_WHITE: CHESS_BLACK;
    _board[chess_row][chess_col] = cur_color;
    //4.判断是否有玩家胜利
    uint64_t winner_id = check_win(chess_row,chess_col,cur_color);

    if(winner_id != 0)
    {
        json_resp["reason"] = "获胜！！";
    }

    json_resp["result"] = true;
    json_resp["winner"] = (Json::UInt64)winner_id;
    return json_resp;
}

////////////////////////////////////////////////////////////////////////////////////////////////


Json::Value room::handle_chat(Json::Value &req)//聊天
{
    Json::Value json_resp = req;
    //1.当前请求的房间号是否于当前的房间的房间号是否匹配
    // uint64_t room_id = req["room_id"].asUInt64();
    // if(room_id != _room_id)
    // {
    //     json_resp["optype"] = req["optype"].asString();
    //     json_resp["result"] = false;
    //     json_resp["reason"] = "房间号不匹配";
    //     return json_resp;
    // }

    std::string msg = req["message"].asCString();
    size_t pos = msg.find("垃圾");
    if(pos != std::string::npos)
    {
        json_resp["result"] = false;
        json_resp["reason"] = "消息中包含敏感词";
        return json_resp;
    }
    
    json_resp["result"] = true;
    return json_resp;
}

void room::handle_exit(uint64_t uid)//玩家退出
{
    Json::Value json_resp;
    if(_statu == GAME_START)
    {
        json_resp["optype"] = "put_chess";
        json_resp["result"] = true;
        json_resp["reason"] = "对手逃跑，获胜！！";
        json_resp["room_id"] = (Json::UInt64)_room_id;
        json_resp["uid"] = (Json::UInt64)uid;
        json_resp["row"] = -1;
        json_resp["col"] = -1;
        json_resp["winner"] = (Json::UInt64)(uid == _white_id ? _black_id : _white_id);
        broadcast(json_resp);
    }
    
    _player_count--;
}

void room::handle_request(Json::Value &req)//区分请求类型，根据不同的请求调用不同的处理函数
{
    Json::Value json_resp;
    uint64_t room_id = req["room_id"].asUInt64();
    if(room_id != _room_id)
    {
        json_resp["optype"] = req["optype"].asString();
        json_resp["result"] = false;
        json_resp["reason"] = "房间号不匹配";
        broadcast(json_resp);
        return;
    }

    if(req["optype"].asString() == "put_chess")
    {
        json_resp = handle_chess(req);
        uint64_t winner_id = json_resp["winner"].asUInt64();
        if(winner_id != 0)
        {
            uint64_t loser_id = winner_id == _white_id ? _black_id : _white_id;
            _tb_user->win(winner_id);
            _tb_user->lose(loser_id);
            _statu = GAME_OVER;
        }
    }
    else if(req["optype"].asString() == "chat")
    {
        json_resp = handle_chat(req);
    }
    else
    {
        json_resp["optype"] = req["optype"].asString();
        json_resp["result"] = false;
        json_resp["reason"] = "未知请求类型！！";
    }
    broadcast(json_resp);
}

void room::broadcast(Json::Value &req)//广播信息到房间所有客户端
{
    //1.对要响应的信息进行序列号
    std::string body;
    json_util::serialize(req,body);
    //2.获取房间中用户的通信连接
    //3.发送响应信息
    wsserver_t::connection_ptr wconn = _online_user->get_conn_from_room(_white_id);
    if(wconn.get() != nullptr)
    {
        wconn->send(body);
    }

    wsserver_t::connection_ptr bconn = _online_user->get_conn_from_room(_black_id);
    if(bconn.get() != nullptr)
    {
        bconn->send(body);
    }

}


bool room::five(int row, int col, int row_off,int col_off, int color)
{
    //row,col是下棋位置，row_off,col_off是偏移量
    int count = 1;
    int search_row = row + row_off;
    int search_col = col + col_off;

    while(search_row >= 0 && search_row < BOARD_ROW &&
          search_col >= 0 && search_col < BOARD_COL &&
          _board[search_row][search_col] == color)
    {
        count++;
        search_row += row_off;
        search_col += col_off;
    }

    search_row = row - row_off;
    search_col = col - col_off;

    while(search_row >= 0 && search_row < BOARD_ROW &&
        search_col >= 0 && search_col < BOARD_COL &&
        _board[search_row][search_col] == color)
    {
        count++;
        search_row -= row_off;
        search_col -= col_off;
    }
    return (count >= 5);
}

uint64_t room::check_win(int row, int col, int color)
{
    if(five(row,col,0,1,color) ||
       five(row,col,1,0,color) ||
       five(row,col,-1,1,color) ||
       five(row,col,-1,-1,color))
    {
        return color == CHESS_WHITE? _white_id : _black_id;
    }
    return 0;
}

using room_ptr = std::shared_ptr<room>;

class room_manager
{
public:
    //初始化房间id计数器
    room_manager(user_table* ut,online_manager*om):
    _next_rid(1),_tb_user(ut),_online_user(om)
    {
        DLOG("房间管理模块初始化完毕");
    }

    ~room_manager()
    {
        DLOG("房间管理模块即将销毁");
    }

    //为两个用户创建一个房间，并添加到房间当中，返回房间的智能指针
    room_ptr create_room(uint64_t uid1,uint64_t uid2);
    //根据房间id获取房间的智能指针
    room_ptr get_room_by_rid(uint64_t rid);
    //根据用户id获取房间的智能指针
    room_ptr get_room_by_uid(uint64_t uid);
    //根据房间id删除房间
    void remove_room(uint64_t rid);
    //根据用户id移除用户，当房间没有用户了就移除房间
    void remove_room_user(uint64_t uid);
private:
    uint64_t _next_rid;
    std::mutex _mutex;

    user_table* _tb_user;
    online_manager* _online_user;

    std::unordered_map<uint64_t,room_ptr> _rooms;
    std::unordered_map<uint64_t,uint64_t> _users;
};

//为两个用户创建一个房间，并添加到房间当中，返回房间的智能指针
room_ptr room_manager::create_room(uint64_t uid1,uint64_t uid2)
{
    //两个用户在大厅匹配成功后创建房间
    //1.检测两个用户是否还在大厅
    if(_online_user->is_in_game_hall(uid1) == false)
    {
        DLOG("用户:%lu 不在大厅中,创建房间失败！！",uid1);
        return room_ptr();
    }

    if(_online_user->is_in_game_hall(uid2) == false)
    {
        DLOG("用户:%lu 不在大厅中,创建房间失败！！",uid2);
        return room_ptr();
    }

    std::unique_lock<std::mutex> lock(_mutex);
    //2.创建房间，将用户添加到房间中
    room_ptr rp(new room(_next_rid,_tb_user,_online_user));
    rp->add_white_user(uid1);
    rp->add_black_user(uid2);
    //3.将房间信息管理起来
    _rooms.insert(std::make_pair(_next_rid,rp));
    _users.insert(std::make_pair(uid1,_next_rid));
    _users.insert(std::make_pair(uid2,_next_rid));

    ILOG("房间创建成功！！！");
    _next_rid++;
    //4.返回房间信息
    return rp;
}

//根据房间id获取房间的智能指针
room_ptr room_manager::get_room_by_rid(uint64_t rid)
{
    std::unique_lock<std::mutex> lock(_mutex);
    auto it = _rooms.find(rid);

    if(it == _rooms.end())
    {
        ILOG("没有找到！！！");
        return room_ptr();
    }
    ILOG("成功找到！！！！");
    return it->second;
}

//根据用户id获取房间的智能指针
room_ptr room_manager::get_room_by_uid(uint64_t uid)
{
    std::unique_lock<std::mutex> lock(_mutex);
    //1.通过用户id获取房间id
    auto uit = _users.find(uid);
    if(uit == _users.end())
    {
        ILOG("没有找到！！！");
        return room_ptr();
    }
    uint64_t rid = uit->second;
    //2.通过房间id来获取房间信息
    auto rit = _rooms.find(rid);
    if(rit == _rooms.end())
    {
        ILOG("没有找到！！！");
        return room_ptr();
    }
    return rit->second;
}

//根据房间id删除房间
void room_manager::remove_room(uint64_t rid)
{
    //1.通过房间id获取房间信息
    room_ptr rp = get_room_by_rid(rid);
    if(rp.get() == nullptr)
    {
        ILOG("删除房间失败，未找到该房间！！！");
        return;
    }

    //2.通过房间信息，获取房间中所有用户的id
    uint64_t uid1 = rp->get_white_user();
    uint64_t uid2 = rp->get_black_user();

    //3.移除房间管理中的用户信息

    std::unique_lock<std::mutex> lock(_mutex);
    _users.erase(uid1);
    _users.erase(uid2);
    //4.移除房间管理信息
    _rooms.erase(rid);
    ILOG("删除房间成功！！");
}

//根据用户id移除用户，当房间没有用户了就移除房间
void room_manager::remove_room_user(uint64_t uid)
{
        //1.通过房间id获取房间信息
    room_ptr rp = get_room_by_uid(uid);
    if(rp.get() == nullptr)
    {
        ILOG("删除房间中用户失败，未找到该房间！！！");
        return;
    }
    rp->handle_exit(uid);
    ILOG("删除房间中用户成功！！");
    //房间中没有玩家了，销毁房间
    if(rp->player_count() == 0)
    {
        remove_room(rp->id());
        ILOG("房间没有玩家，销毁该房间");
    }
}



#endif
